﻿using MongoDB.Bson;
using MongoDB.Driver;
using Sustainalytics.Auth.Claims;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer.ConnectionStrings;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.Entities.Governance;
using Sustainalytics.Governance.BusinessLogic.Model;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Cors;

namespace Sustainalytics.Governance.Service.Controllers.Listing
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/companies")]
    public class ListingController : ApiController
    {
        private readonly IMongoCollection<CorporateData> _corporateDataCollection;
        private readonly IMongoCollection<GovernanceComputationResult> _corporateGovernanceProfileCollection;

        public ListingController()
        {

            var connectionString = ConfigurationManager.ConnectionStrings["ClientPlatform"].ConnectionString;
            var connectionStringsManager =
                ConnectionStringsManager.GetInstance(ConnectionStringsRepository.GetInstance(connectionString));
            var corporateDataConnectionString = connectionStringsManager.GetConnectionString(ConnectionStringType.ClientDW);
            var governanceConnectionString = connectionStringsManager.GetConnectionString(ConnectionStringType.GovernanceDW);

            _corporateDataCollection = MongoFactory.GetCollectionFromDatabase<CorporateData>(corporateDataConnectionString);
            _corporateGovernanceProfileCollection = MongoFactory.GetCollectionFromDatabase<GovernanceComputationResult>(governanceConnectionString);
        }

        private static readonly Dictionary<string, Expression<Func<CorporateData, object>>> sortDictionary = new Dictionary<string, Expression<Func<CorporateData, object>>>
        {
            { "CompanyName", x => x.CompanyName },
            { "Exchange", x => x.MainExchange },
            { "Ticker", x => x.MainTicker },
            { "Subindustry", x => x.SubPeerGroup },
            { "Performance", x => x.CompanyName }
        };

        private List<FilterDefinition<CorporateData>> getFilterOptionsForCompanyData(GovernanceListFilters filters)
        {
            var filtersCompanies = Builders<CorporateData>.Filter;
            var governanceFiltersList = new List<FilterDefinition<CorporateData>> { FilterDefinition<CorporateData>.Empty };

            if (!filters.CompanyName.IsNullOrEmpty())
            {
                var filterCompany = filtersCompanies.Regex("CompanyName", new BsonRegularExpression(Regex.Escape(filters.CompanyName.ToLowerInvariant()), "i"));
                governanceFiltersList.Add(filterCompany);
            }

            if (!filters.SubPeerGroup.IsNullOrEmpty())
            {
                var filterSubPeerGroup = filtersCompanies.In(x => x.SubPeerGroup, filters.SubPeerGroup);
                governanceFiltersList.Add(filterSubPeerGroup);
            }

            return governanceFiltersList;
        }

        private List<FilterDefinition<GovernanceComputationResult>> getFilterOptionsForPerformance(GovernanceListFilters filters)
        {
            var filtersCompanies = Builders<GovernanceComputationResult>.Filter;
            var governanceFiltersList = new List<FilterDefinition<GovernanceComputationResult>> { FilterDefinition<GovernanceComputationResult>.Empty };

            if (!filters.Assessments.IsNullOrEmpty())
            {
                var filterAssessments = filtersCompanies.In(x => (int)x.OverallPerformance.Assessment, filters.Assessments);
                governanceFiltersList.Add(filterAssessments);
            }

            return governanceFiltersList;
        }

        [HttpGet]
        [Route("")]
        [InjectUniverse("universeIds", AccountFeature.Governance)]
        public async Task<IHttpActionResult> Get(
            [FromUri] GovernanceListFilters filters,
            int skip = 0,
            int take = 20,
            bool getCount = false,
            bool orderAscending = true,
            string orderBy = "CompanyName",
            [FromUri]int[] universeIds = null
            )
        {
            var filterOptionsForCompanyData = getFilterOptionsForCompanyData(filters);
            FindOptions options = new FindOptions() { Collation = new Collation(locale: "en", caseLevel: true, strength: CollationStrength.Primary) };

            var filterOptionsForPerformance = getFilterOptionsForPerformance(filters);

            var companiesPerformance = await _corporateGovernanceProfileCollection
                 .Find(Builders<GovernanceComputationResult>.Filter.And(filterOptionsForPerformance))
                 .Project<GovernanceComputationResult>(Builders<GovernanceComputationResult>.Projection
                 .Include(x => x.Id)
                 .Include(x => x.OverallPerformance.Assessment))
                 .ToListAsync().ConfigureAwait(false);

            var filteredCompaniesPerformance = companiesPerformance.Select(x => x.Id).ToList();


            var builderCorporateData = Builders<CorporateData>.Filter;
            var filter = (builderCorporateData.Eq(x => x.CorporateGovernanceResearchState.ResearchState, ResearchState.ResearchAvailable)) &
                    builderCorporateData.And(filterOptionsForCompanyData) &
                    builderCorporateData.In(x => x.Id, filteredCompaniesPerformance);


            var companiesCollection = _corporateDataCollection
                .Find(filter, options)
                .Project<CorporateData>(Builders<CorporateData>.Projection
                    .Include(x => x.Id)
                    .Include(x => x.CompanyName)
                    .Include(x => x.MainExchange)
                    .Include(x => x.MainTicker)
                    .Include(x => x.SubPeerGroup)
                    .Include(x => x.UniverseIds));

            if (getCount)
            {
                // get companies paged
                var count = await _corporateDataCollection
                    .Find(filter, options)
                    .CountAsync()
                    .ConfigureAwait(false);

                return Ok(new { companyCount = count });
            }
            var sortDefinition = orderAscending ?
                Builders<CorporateData>.Sort.Ascending(sortDictionary[orderBy]) :
                Builders<CorporateData>.Sort.Descending(sortDictionary[orderBy]);

            var companiesList = await companiesCollection
                .Sort(sortDefinition)
                .Skip(skip)
                .Limit(take)
                .ToListAsync().ConfigureAwait(false);

            var result = companiesList.Select(x => new
            {
                x.Id,
                x.CompanyName,
                Exchange = x.MainExchange,
                Ticker = x.MainTicker,
                x.SubPeerGroup,
                Performance = companiesPerformance.Where(p => p.Id == x.Id).Select(perf => (int)perf.OverallPerformance.Assessment).FirstOrDefault() + 1,
                IsEnabled = universeIds.Any(y => x.UniverseIds.Contains(y))
            });

            return Ok(result);
        }
    }
}